package com.linq.cool.system.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.linq.cool.api.domain.SysRole;
import com.linq.cool.common.constants.UserConstants;
import com.linq.cool.common.core.exception.BusinessException;
import com.linq.cool.common.core.utils.BeanUtils;
import com.linq.cool.common.core.utils.SecurityUtils;
import com.linq.cool.common.core.utils.StringUtils;
import com.linq.cool.common.datascope.annotation.DataScope;
import com.linq.cool.system.domain.SysRoleDept;
import com.linq.cool.system.domain.SysRoleMenu;
import com.linq.cool.system.domain.SysUserRole;
import com.linq.cool.system.dto.SysRoleDTO;
import com.linq.cool.system.dto.SysRoleDataScopeDTO;
import com.linq.cool.system.dto.SysRoleQueryDTO;
import com.linq.cool.system.mapper.SysRoleMapper;
import com.linq.cool.system.service.SysRoleDeptService;
import com.linq.cool.system.service.SysRoleMenuService;
import com.linq.cool.system.service.SysRoleService;
import com.linq.cool.system.service.SysUserRoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * @Author: yqlin
 * @Date: 2020/12/29 19:17
 * @Description:
 * @Version: 1.0.0
 */
@Service
public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> implements SysRoleService {
    @Autowired
    private SysRoleMenuService sysRoleMenuService;
    @Autowired
    private SysRoleDeptService sysRoleDeptService;
    @Autowired
    private SysUserRoleService sysUserRoleService;

    @Override
    public Set<String> listRolePermissionByUserId(Long userId) {
        List<SysRole> roleList = baseMapper.listRolePermissionByUserId(userId);
        Set<String> roleSet = new HashSet<>();
        for (SysRole sysRole : roleList) {
            if (StringUtils.isNotNull(sysRole)) {
                roleSet.addAll(Arrays.asList(sysRole.getPermCode().trim().split(",")));
            }
        }
        return roleSet;
    }

    @Override
    public SysRole getRoleById(Long roleId) {
        return baseMapper.getRoleById(roleId);
    }

    @DataScope(deptAlias = "d")
    @Override
    public IPage<SysRole> listByPage(IPage<SysRole> page, SysRoleQueryDTO dto) {
        return baseMapper.listByPage(page, dto);
    }

    @Override
    public boolean checkNameUnique(SysRole role) {
        // 判断传入的是否是空(新增或者更细) 新增的时候id roleId=-1  更新就是原来的
        Long roleId = null == role.getId() ? -1L : role.getId();
        SysRole one = this.getOne(new LambdaQueryWrapper<SysRole>()
                                          .eq(SysRole::getName, role.getName())
        );
        // 判断id是否一致 不一致就代表 唯一 false
        return null != one && !roleId.equals(one.getId());
    }

    @Override
    public boolean checkPermCodeUnique(SysRole role) {
        // 判断传入的是否是空(新增或者更细) 新增的时候id roleId=-1  更新就是原来的
        Long roleId = null == role.getId() ? -1L : role.getId();
        SysRole one = this.getOne(new LambdaQueryWrapper<SysRole>()
                                          .eq(SysRole::getPermCode, role.getPermCode())
        );
        // 判断id是否一致 不一致就代表 唯一 false
        return null != one && !roleId.equals(one.getId());
    }

    @Override
    public void checkAllowed(SysRole role) {
        if (StringUtils.isNotNull(role.getId()) && role.isAdmin()) {
            throw new BusinessException("不允许操作超级管理员角色");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateDataScope(SysRoleDataScopeDTO dto) {
        SysRole role = new SysRole();
        BeanUtils.copyProperties(dto, role);
        this.checkAllowed(role);
        role.setModifier(SecurityUtils.getUserId()).setGmtModified(new Date());
        // 修改角色信息
        this.updateById(role);
        // 删除角色与部门关联
        sysRoleDeptService.remove(new LambdaQueryWrapper<SysRoleDept>().eq(SysRoleDept::getRoleId, role.getId()));
        // 新增角色和部门信息（数据权限）
        return this.saveRoleDept(role.getId(), dto.getDeptIds());
    }

    @Override
    public boolean deleteRoleByIds(List<Long> roleIds) {
        roleIds.stream().filter(Objects::nonNull).forEach(roleId -> {
            this.checkAllowed(new SysRole(roleId));
            SysRole role = this.getById(roleId);
            int count = sysUserRoleService.count(new LambdaQueryWrapper<SysUserRole>().eq(SysUserRole::getRoleId, roleId));
            if (count > 0) {
                throw new BusinessException(String.format("%1$s已分配,不能删除", role.getName()));
            }
        });
        return this.removeByIds(roleIds);
    }

    @Override
    public List<Long> listByUserId(Long userId) {
        return baseMapper.listByUserId(userId);
    }

    @Override
    public List<SysRole> listByParams(SysRole role) {
        return this.list(this.getLambdaQueryWrapper(role));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean saveRole(SysRoleDTO dto) {
        SysRole role = new SysRole();
        BeanUtils.copyProperties(dto, role);
        if (this.checkNameUnique(role)) {
            throw new BusinessException("新增角色'" + role.getName() + "'失败，角色名称已存在");
        }
        if (this.checkPermCodeUnique(role)) {
            throw new BusinessException("新增角色'" + role.getName() + "'失败，角色权限已存在");
        }
        role.setCreator(SecurityUtils.getUserId());
        // 新增角色信息
        this.save(role);
        // 新增角色菜单中间表信息
        return this.saveRoleMenu(role.getId(), dto.getMenuIds());
    }

    @Override
    public boolean updateRole(SysRoleDTO dto) {
        SysRole role = new SysRole();
        BeanUtils.copyProperties(dto, role);
        this.checkAllowed(role);
        if (this.checkNameUnique(role)) {
            throw new BusinessException("修改角色'" + role.getName() + "'失败，角色名称已存在");
        }
        if (this.checkPermCodeUnique(role)) {
            throw new BusinessException("修改角色'" + role.getName() + "'失败，角色权限已存在");
        }
        role.setModifier(SecurityUtils.getUserId()).setGmtModified(new Date());
        // 修改角色信息
        this.updateById(role);
        // 删除角色与菜单关联信息
        sysRoleMenuService.remove(new LambdaQueryWrapper<SysRoleMenu>().eq(SysRoleMenu::getRoleId, role.getId()));
        // 新增关联信息
        return this.saveRoleMenu(role.getId(), dto.getMenuIds());
    }

    @Override
    public List<SysRole> listRolesByUserId(Long userId) {
        return baseMapper.listRolesByUserId(userId);
    }

    private LambdaQueryWrapper<SysRole> getLambdaQueryWrapper(SysRole role) {
        return new LambdaQueryWrapper<SysRole>()
                .eq(SysRole::getIsDeleted, UserConstants.StateEnum.STATUS_NORMAL.getCode())
                .like(com.baomidou.mybatisplus.core.toolkit.StringUtils.checkValNotNull(role.getName()), SysRole::getName, role.getName())
                .eq(com.baomidou.mybatisplus.core.toolkit.StringUtils.checkValNotNull(role.getStatus()), SysRole::getName, role.getStatus())
                .eq(com.baomidou.mybatisplus.core.toolkit.StringUtils.checkValNotNull(role.getPermCode()), SysRole::getName, role.getName());
    }

    /**
     * 新增角色部门信息(数据权限)
     */
    private boolean saveRoleDept(Long roleId, List<Long> deptIds) {
        if (CollectionUtils.isNotEmpty(deptIds)) {
            // 新增角色与部门（数据权限）管理
            List<SysRoleDept> list = new ArrayList<>();
            deptIds.forEach(deptId -> {
                SysRoleDept rd = new SysRoleDept();
                rd.setDeptId(deptId).setRoleId(roleId);
                list.add(rd);
            });
            return list.size() > 0 && sysRoleDeptService.saveBatch(list);
        }
        return true;
    }


    /**
     * 新增角色菜单信息
     */
    private boolean saveRoleMenu(Long roleId, List<Long> menuIds) {
        if (CollectionUtils.isNotEmpty(menuIds)) {
            List<SysRoleMenu> list = new ArrayList<>();
            menuIds.stream().filter(Objects::nonNull).forEach(menuId -> {
                SysRoleMenu rm = new SysRoleMenu();
                rm.setRoleId(roleId).setMenuId(menuId);
                list.add(rm);
            });
            return list.size() > 0 && sysRoleMenuService.saveBatch(list);
        }
        return true;
    }
}

